前面我們學了express這個nodeJS的框架
express的優點是輕量、簡易
今天要來講解另一個更適合用來開發大型專案的NodeJS框架:NestJS
先來了解一下express和nestJS之間的比較差異
當然可以!讓我以繁體中文詳細解釋 Express 和 NestJS 的特色以及優劣點:
Express:
特色:
- 輕量級框架:Express 是一個精簡且靈活的框架,它提供了基本的路由和中介軟體功能,但讓開發者自由選擇其他模組和工具。
- 大型社群和生態系統:Express 擁有龐大的開發者社群和豐富的第三方套件,可以解決各種開發需求。
- 容易上手:對於初學者來說,Express 的學習曲線相對較低,並且提供了豐富的文檔和教學資源。
優點:
- 彈性高:Express 讓你可以很自由地設計你的應用程式架構,可以完全按照你的需求來定制。
- 適用於小型專案:對於小型專案或者需要快速開發的場景,Express 是一個很好的選擇。
- 適合API開發:Express 在處理 RESTful API 非常出色,可以輕鬆地處理 HTTP 請求和響應。
缺點:
- 缺乏組織結構:對於大型或者需要嚴格的組織結構的專案來說,Express 可能需要額外的設計和配置。
- 容易產生程式碼重複:在大型專案中,開發者需要特別注意結構的設計,以免產生重複的程式碼。
NestJS:
特色:
- 基於Express:NestJS 建立在 Express 的基礎上,提供了更加結構化和模組化的開發方式,同時保留了 Express 的彈性。
- 強調模組化和依賴注入:NestJS 鼓勵開發者使用模組化的方式組織程式碼,並且通過依賴注入實現了鬆散耦合。
- 支援 TypeScript:NestJS 原生支援 TypeScript,這使得開發者可以利用 TypeScript 的強大功能來開發應用程式。
優點:
- 結構化和模組化:NestJS 提供了清晰的架構和模組化的方式,適合開發大型專案和團隊合作。
- 依賴注入:這種設計模式讓程式碼更容易測試和維護,並且降低了模組之間的耦合度。
- 強大的中間件支援:NestJS 提供了豐富的中間件選項,方便進行諸如驗證、日誌記錄等操作。
缺點:
- 學習曲線較高:相對於 Express,NestJS 的學習曲線可能較高,特別是對於初學者來說。
- 額外的抽象層:雖然提供了許多便利的功能,但也可能增加了一些額外的抽象層,需要開發者花時間熟悉。
綜合來看,Express 適合小型專案和需要快速開發的場景,而 NestJS 則更適合大型專案和需要良好結構化的場景,特別是在團隊協作方面。選擇哪一個取決於你的專案需求和開發團隊的經驗水平。
當然,讓我詳細介紹 NestJS 給你:
NestJS 是一個用於建立可擴展且高效的 Node.js 應用程式的框架。它結合了 TypeScript 的強型別語法和依賴注入的概念,以提供一個結構化、模組化的方式來開發後端應用程式。以下是一些 NestJS 的關鍵特點:
- 基於 TypeScript: NestJS 完全基於 TypeScript,這意味著你可以使用類型安全的語言來開發應用程式,減少錯誤和提高代碼品質。
- 模組化結構: NestJS 採用模組化的結構,這有助於將應用程式分成多個獨立的模組,使代碼更容易管理和擴展。每個模組包含控制器、服務、和提供者,這有助於保持代碼的整潔性。
- 依賴注入: NestJS 提供了依賴注入容器,使你可以輕鬆管理應用程式的各種組件之間的相依關係。這提高了代碼的可測試性和可維護性。
- 中介軟體和管道: NestJS 支持中介軟體和管道,這些功能可以用於處理請求和響應,執行驗證、日誌記錄等操作,以保護應用程式的安全性和效能。
- 路由管理: NestJS 提供了強大的路由管理功能,可以輕鬆定義和組織路由,並映射到相應的控制器方法。
- 自動文件生成: NestJS 通過 Swagger 自動生成 API 文件,這有助於開發者和團隊理解和測試 API。
- 支援多種資料庫: NestJS 兼容多種資料庫,包括 MySQL、PostgreSQL、MongoDB 等,並提供相應的模組和套件,以便與資料庫進行交互。
- 豐富的生態系統: NestJS 生態系統提供了大量的模組和套件,可用於處理身份驗證、授權、緩存、日誌記錄等常見的後端需求。
- 測試友好: 由於其模組化結構和依賴注入,NestJS 非常適合測試。你可以輕鬆地編寫單元測試和端對端測試,以確保代碼的品質和穩定性。
總之,NestJS 是一個強大且優雅的框架,特別適合需要構建可擴展、模組化、且高效的 Node.js 應用程式的開發者。它結合了 TypeScript 的強型別語法和依賴注入的優勢,使你能夠以更有組織和可維護的方式開發後端應用程式。
了解以上之後,我們來開始學習nestJS
另外溫馨提醒,如果nodeJS版本低於16的話,安裝的時候會跑出一些錯誤
若有其他專案因素無法隨意升級,那麼你跟我一樣,需要的都是:
【Day41】ChatGPT請教教我:nvm!Node.js 版本控管!
沒問題我們就繼續!
好的,讓我為你提供一份完整的 NestJS 教學,包括運作流程和基本概念的解說。由於篇幅的限制,我將提供一個簡單的範例來幫助你了解 NestJS 的基本操作。
NestJS 安裝與建立專案:
首先,確保你已經安裝了 Node.js 和 npm。然後,透過以下命令安裝 Nest CLI(命令列工具):
npm install -g @nestjs/cli
接著,建立一個新的 NestJS 專案:
nest new project-name
這個命令將建立一個名為
project-name
的新專案。運作流程:
NestJS 的運作流程主要包括以下幾個基本概念:
- 模組 (Module): NestJS 使用模組來組織應用程式的結構。一個模組可以包含控制器、提供者和其他相關元件。模組可以透過依賴注入來引用其他模組。
- 控制器 (Controller): 控制器負責處理進來的 HTTP 請求並發送適當的回應。它們會定義路由(端點)以及處理請求的方法。
- 提供者 (Provider): 提供者是一個常見的概念,用於處理業務邏輯、資料庫操作等。它們可以在控制器中被引用。
- 依賴注入 (Dependency Injection): NestJS 使用依賴注入來管理模組之間的相依關係。這讓你可以輕鬆地將相依關係傳遞給其他元件。
- 中介軟體 (Middleware): 中介軟體可以在請求到達控制器之前或之後進行處理。這使你可以執行額外的邏輯,例如驗證、日誌記錄等。
簡單範例:
讓我們創建一個簡單的示例來演示這些概念:
- 建立一個模組:
nest generate module cats
這將在
src
目錄下創建一個名為cats
的模組。
- 建立一個控制器:
nest generate controller cats
這將在
cats
模組中創建一個名為cats.controller.ts
的控制器。
- 建立一個提供者:
nest generate service cats
這將在
cats
模組中創建一個名為cats.service.ts
的提供者。
- 編輯控制器和提供者:
打開
cats.controller.ts
和cats.service.ts
,並在其中編寫相應的程式碼。例如:// cats.controller.ts import { Controller, Get } from '@nestjs/common'; import { CatsService } from './cats.service'; @Controller('cats') export class CatsController { constructor(private readonly catsService: CatsService) {} @Get() findAll(): string[] { return this.catsService.findAll(); } } // cats.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class CatsService { private readonly cats: string[] = ['Cat 1', 'Cat 2', 'Cat 3']; findAll(): string[] { return this.cats; } }
- 在模組中引用控制器和提供者:
打開
cats.module.ts
,並將控制器和提供者引用進來:// cats.module.ts import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule {}
- 啟動應用程式:
執行以下命令以啟動應用程式:
npm run start
- 發送請求:
現在你可以使用 API 測試工具(例如 Postman)或瀏覽器來發送 GET 請求至
http://localhost:3000/cats
,你應該會得到包含所有貓咪名字的回應。這只是一個簡單的範例,但它涵蓋了 NestJS 的基本概念,包括模組、控制器、提供者和依賴注入。希望這份教學能幫助你開始使用 NestJS 建立你自己的應用程式!如果你有任何問題或需要進一步的解釋,請隨時告訴我。
照著做之後,首先檔案結構發生了變動,多出了cats資料夾與下列的檔案
且app.module.ts也多出了引入CatsModule的程式碼
接著打開 http://localhost:3000/cats 也看到了service所提供的「貓列表」
上面的範例主要是「module.ts
」、「controller.ts
」、「service.ts
」組成的
我們用更簡單的方式了解一下這些東西怎麼運作
當然,我將使用 Mermaid 來繪製一個專案中的 Module、Controller、Service 的關係圖,並進行易懂的比喻解說。
graph TD; A[NestJS Application] -->|包含| B[Module1] A -->|包含| C[Module2] B -->|包含| D[Controller1] C -->|包含| E[Controller2] B -->|包含| F[Service1] C -->|包含| G[Service2] D -->|使用| F E -->|使用| G
解說:
- NestJS Application(NestJS 應用程式) 是整個 NestJS 專案的主要架構,它包含了所有的模組、控制器和服務。
- Module(模組) 可以想像成專案中的一個功能區塊,比如說一個貓咪管理模組,它可能包括一個控制器用來處理貓咪相關的請求,以及一個服務用來處理業務邏輯。
- Controller(控制器) 像是一個路由管理員,它接收進來的請求,然後根據請求的路徑和方法(GET、POST等)來執行相應的處理邏輯。比如說,如果你想要獲取所有貓咪的資訊,你會發送一個 GET 請求到
cats
路徑,這個控制器就會負責處理這個請求。- Service(服務) 就像是一個工廠,負責處理具體的業務邏輯。在我們的貓咪管理模組中,可能會有一個服務用來處理貓咪相關的資料庫操作,比如獲取所有貓咪的資料。
總的來說,NestJS 的架構讓你可以將一個大型的應用程式拆分成多個模組,每個模組都有自己的控制器和服務,這樣使得代碼更有組織性和可維護性。控制器接收請求,服務處理業務邏輯,而模組則用來組織這些元件。
希望這個比喻和圖表能夠幫助你理解 NestJS 的基本概念和流程!如果你有任何進一步的問題,請隨時告訴我。
ChatGPT繼續用貓來當作例子說故事蠻可愛的,雖然不明所以但很可愛
再來一張範例了解請求傳遞與處理流程
好的,讓我使用 Mermaid 來表示接收 request 到回傳結果的流程。
graph LR; A[Client] -->|發送請求| B[Controller]; B -->|呼叫Service| C[Service]; C -->|處理業務邏輯| D[Database]; D -->|回傳結果| C; C -->|回傳結果| B; B -->|回傳結果| A;
解說:
- Client (客戶端) 發送請求至 NestJS 應用程式。
- Controller (控制器) 接收到請求,並根據請求的路徑和方法選擇相應的處理邏輯。
- Service (服務) 被控制器呼叫,處理具體的業務邏輯,可能包括存取資料庫等操作。
- Database (資料庫) 處理服務可能需要的資料庫操作。
- 當 Service 完成業務邏輯處理後,回傳結果給 Controller。
- Controller 將處理結果回傳給 Client。
這個流程表達了一個基本的請求處理過程:從客戶端發送請求,經過控制器、服務、資料庫,再回到服務和控制器,最終回傳結果給客戶端。
希望這個流程圖和解說能幫助你更好地理解 NestJS 中請求的處理過程!如果你有任何進一步的問題,請隨時告訴我。
當然,讓我針對 NestJS 中的 Module(模組)提供詳細的解釋:
Module(模組)的概念:
在 NestJS 中,模組是一個組織程式碼的基本單位,它可以被視為一個功能或一個相關聯的集合。一個模組可以包含控制器、服務、提供者等元件,這些元件一起協同工作以實現特定功能。模組的主要目的是提供程式碼的組織和封裝,使得代碼更容易維護和擴展。
如何建立一個 Module:
在 NestJS 中,你可以使用 Nest CLI 或手動創建模組。
- 使用 Nest CLI 創建模組:
nest generate module module-name
這個命令會在
src
目錄下創建一個名為module-name
的模組。
- 手動創建模組:
你可以自行創建一個包含相應的
.module.ts
檔案的目錄,並在其中定義模組。模組的設定與語法:
一個模組通常由一個類別來表示,該類別使用
@Module()
裝飾器來標識自己是一個模組。import { Module } from '@nestjs/common'; @Module({ controllers: [], // 控制器 providers: [], // 服務或提供者 imports: [], // 引入其他模組 exports: [], // 將元件暴露給其他模組使用 }) export class MyModule {}
controllers
屬性用於列舉屬於這個模組的控制器。providers
屬性包含了這個模組的提供者,如服務。imports
屬性可以引入其他模組,讓這個模組可以使用其他模組中的元件。exports
屬性允許你將這個模組的特定元件暴露給其他模組使用。注入相關的解釋與說明:
在 NestJS 中,依賴注入是一個重要的概念。它使得我們可以將一個元件的相依關係委託給 NestJS 的容器,這樣可以更容易地測試和維護程式碼。
例如,在一個控制器中,你可以透過構造函數來請求相依的服務:
import { Controller } from '@nestjs/common'; import { MyService } from './my-service.service'; @Controller() export class MyController { constructor(private readonly myService: MyService) {} }
在上面的例子中,
MyController
類別需要一個MyService
的實例來執行它的業務邏輯。當模組中的MyService
提供者被建立時,它會被注入到MyController
中。這使得我們可以更容易地測試
MyController
,因為我們可以用假的MyService
來測試MyController
的行為。總的來說,模組的設定與語法以及依賴注入是 NestJS 中的基礎概念,它們使得我們可以編寫可測試、模組化和可擴展的程式碼。希望這份解釋能幫助你更好地理解 NestJS 中的模組相關概念!如果你有任何進一步的問題,請隨時告訴我。
所以用多個module把功能分類,每個module內都只處理相關的邏輯
透過 module.ts 來定義該module的各角色,把 controllers 和相關的service 引入
也可以針對該module把特定元件給export匯出
但這邊就有一個概念需要釐清了
上面有providers
與 imports
兩種欄位,都和這個module可以使用的service 有關
這兩者的差異是什麼呢?
當你在 NestJS 中建立模組時,你可能會使用
providers
和imports
來定義模組的相依性和功能。這兩個屬性有不同的用途和影響程式的方式:Providers(提供者):
providers
是模組中定義的元件,它們可以是服務、資料庫連接、庫等。這些提供者是模組內的私有元件,只有在同一模組內才能使用。在模組內,你可以將這些提供者注入到控制器、服務或其他提供者中,以實現相關的功能。使用方式:
- 在模組中定義提供者: 你可以在模組的
providers
屬性中定義提供者,如服務。import { Module } from '@nestjs/common'; import { MyService } from './my-service.service'; @Module({ providers: [MyService], }) export class MyModule {}
- 注入提供者: 你可以在控制器、服務或其他提供者中使用
constructor
依賴注入的方式,將提供者注入到你的類別中。import { Controller, Get } from '@nestjs/common'; import { MyService } from './my-service.service'; @Controller() export class MyController { constructor(private readonly myService: MyService) {} @Get() getSomething() { return this.myService.doSomething(); } }
Imports(引入模組):
imports
用於引入其他模組的功能,這樣你的模組就可以使用其他模組中定義的元件,包括提供者。這是 NestJS 中實現模組重用和組合功能的重要方式。使用方式:
- 在模組中引入其他模組: 你可以在模組的
imports
屬性中引入其他模組,以使用它們的功能。import { Module } from '@nestjs/common'; import { MyService } from './my-service.service'; import { OtherModule } from './other.module'; // 引入其他模組 @Module({ providers: [MyService], imports: [OtherModule], // 引入其他模組 }) export class MyModule {}
- 使用其他模組中的提供者: 一旦你引入了其他模組,你可以在你的模組中使用它們的提供者。這些提供者將可用於你的控制器、服務等元件。
import { Controller, Get } from '@nestjs/common'; import { MyService } from './my-service.service'; import { OtherService } from 'other-service'; // 使用其他模組的提供者 @Controller() export class MyController { constructor( private readonly myService: MyService, private readonly otherService: OtherService, // 使用其他模組的提供者 ) {} @Get() getSomething() { return this.myService.doSomething() + this.otherService.doSomething(); } }
注入方式的差異:
- Providers(提供者)的注入: 提供者是在同一模組內注入的,並且可以輕鬆使用相同模組內的其他提供者,因為它們都在相同的模組範圍內。這種注入方式適用於模組內的功能,如控制器和服務之間的相依性。
- Imports(引入模組)的注入: 通過引入其他模組,你可以跨模組範圍注入其他模組中的提供者。這允許你使用來自不同模組的提供者,並組合它們以實現更大的功能,這是實現模組重用和組合的方式。
總結來說,
providers
用於模組內部的元件相依性,而imports
用於引入其他模組的功能,以擴展你的模組的能力。這樣的組合方式使得你可以編寫更具組織性和可維護性的 NestJS 應用程式。
從 Controller 裡看起來兩者沒有差別,都是注入在其中可以使用的 Service
但一個是和本身相關的Service,處理和該module相關的功能
一個是從其他模組引入,要看該模組匯出了什麼(通常也是一些Service)
再請ChatGPT用一些情境來解釋好了
好的,讓我透過一個實際情境的例子來解釋
providers
和imports
的差異。假設我們正在開發一個簡單的電子商務網站,我們有兩個模組:
- ProductModule (商品模組) 負責處理商品相關的功能,包括商品的控制器、服務以及商品數據庫連接。
- OrderModule (訂單模組) 負責處理訂單相關的功能,包括訂單的控制器、服務以及訂單數據庫連接。
使用
providers
的情境:ProductModule:
在
ProductModule
中,我們定義了商品相關的控制器、服務和提供者。// product.module.ts import { Module } from '@nestjs/common'; import { ProductService } from './product.service'; import { ProductController } from './product.controller'; @Module({ providers: [ProductService], controllers: [ProductController], }) export class ProductModule {}
在這個模組中,我們有一個
ProductService
用於處理商品相關的業務邏輯。OrderModule:
在
OrderModule
中,我們也有一個類似的情況。// order.module.ts import { Module } from '@nestjs/common'; import { OrderService } from './order.service'; import { OrderController } from './order.controller'; @Module({ providers: [OrderService], controllers: [OrderController], }) export class OrderModule {}
在這個模組中,我們有一個
OrderService
用於處理訂單相關的業務邏輯。使用
imports
的情境:現在,我們希望在訂單模組中可以使用商品相關的功能,比如在訂單中添加商品。
OrderModule:
為了實現這個目標,我們可以將
ProductModule
引入到OrderModule
中。// order.module.ts import { Module } from '@nestjs/common'; import { OrderService } from './order.service'; import { OrderController } from './order.controller'; import { ProductModule } from '../product/product.module'; // 引入ProductModule @Module({ imports: [ProductModule], // 引入ProductModule providers: [OrderService], controllers: [OrderController], }) export class OrderModule {}
現在,
OrderModule
中就可以使用來自ProductModule
的提供者,包括ProductService
。// order.service.ts import { Injectable } from '@nestjs/common'; import { ProductService } from '../product/product.service'; // 引入ProductService @Injectable() export class OrderService { constructor(private readonly productService: ProductService) {} addProductToOrder(productId: string) { const product = this.productService.getProductById(productId); // 其他處理邏輯... } }
在上面的例子中,我們在
OrderService
中使用了來自ProductModule
的ProductService
來獲取商品的資訊。總的來說,使用
providers
來定義模組內部的元件相依性,而使用imports
來引入其他模組的功能,使得你的模組能夠使用其他模組中的提供者,這樣就可以實現模組的重用和組合。
範例很實際,夠實際
所以providers
放該模組應該要處理的內容imports
放其他模組有提供過的內容,但是需要重複使用邏輯的部分
再來個結構圖
當然,我會使用 Mermaid 來表示上述的結構關係。
graph TB; A[Client] -->|發送請求| B[OrderController]; B -->|呼叫OrderService| D[OrderService]; D -->|使用ProductService| F[ProductService]; F -->|執行業務邏輯| G[ProductDatabase]; G -->|回傳結果| F; D -->|回傳結果| B; B -->|回傳結果| A; H[ProductModule] -->|提供ProductService| F; I[OrderModule] -->|引入ProductModule| H;
解說:
- Client (客戶端) 發送請求至
OrderController
。- OrderController 接收到請求,並呼叫
OrderService
進行處理。- OrderService 需要使用
ProductService
來執行業務邏輯。- ProductService 使用
ProductDatabase
來取得商品相關資料。- ProductDatabase 回傳結果給
ProductService
。- ProductService 將結果回傳給
OrderService
。- OrderService 將結果回傳給
OrderController
。- 最終,
OrderController
將結果回傳給客戶端。此外,可以看到
OrderModule
引入了ProductModule
,這使得在OrderModule
中可以使用ProductService
。希望這個 Mermaid 圖表能幫助你更好地理解上述情境的結構關係!如果你有任何進一步的問題,請隨時告訴我。
在你提供的模組中,
providers
和imports
可以被注入到以下元件中:
- OrderService (訂單服務) 可以使用
ProductService
。// order.service.ts import { Injectable } from '@nestjs/common'; import { ProductService } from 'product/product.service'; // 引入ProductService @Injectable() export class OrderService { constructor(private readonly productService: ProductService) {} // 使用 productService 進行相關操作 }
- OrderController (訂單控制器) 可以使用
OrderService
,同時也可以使用ProductService
。// order.controller.ts import { Controller, Get } from '@nestjs/common'; import { OrderService } from './order.service'; import { ProductService } from 'product/product.service'; // 引入ProductService @Controller() export class OrderController { constructor( private readonly orderService: OrderService, private readonly productService: ProductService, // 注入ProductService ) {} @Get() getSomething() { // 使用 orderService 和 productService 進行相關操作 } }
- ProductService (商品服務) 不能使用其他模組中的提供者,因為它是
ProductModule
內的提供者,而且ProductModule
並沒有被其他模組引入。這就是在你提供的模組中,
providers
和imports
可以被注入的元件以及相對應的舉例。在這個情境下,OrderService
和OrderController
可以使用ProductService
,因為ProductModule
被引入到OrderModule
中。
在 NestJS 中,模組(Module)是程式碼組織的基本單位,可以被視為一個功能或相關聯的集合。模組使用
@Module()
裝飾器來標識自己,並且可以包含控制器、服務和提供者等元件。以下是模組相關的重點:
- Module的概念:模組是程式碼組織的基本單位,用於封裝相關功能。
- 如何建立Module:可以使用 Nest CLI 或手動創建模組。
- Module的設定與語法:模組使用
@Module()
裝飾器進行標識,可以包含 controllers、providers、imports 和 exports 屬性,分別用於定義控制器、提供者、引入其他模組和導出元件。- Providers的作用:在模組中定義的元件,例如服務、資料庫連接等。這些提供者是模組內的私有元件,只能在同一模組中使用。
- Imports的作用:用於引入其他模組的功能,使得模組可以使用其他模組中定義的元件,包括提供者。
- 注入的方式差異:使用
providers
注入的元件只能在同一模組中使用,而使用imports
引入的元件可以在不同模組間共用。- 實際情境的例子:以電子商務網站為例,商品模組和訂單模組可以相互引入,使得訂單模組可以使用商品相關的功能。
綜合來看,NestJS 的模組概念和設定讓我們能夠更有效地組織程式碼,實現功能模組化和重用性。透過提供者和引入其他模組,我們可以建立出組織有序、易於擴展的應用程式。
希望這份統整能幫助你更清晰地理解 NestJS 中的模組相關概念!如果你有任何進一步的問題,請隨時告訴我。
本次對話紀錄在這邊:
https://chat.openai.com/share/16baa5d0-3895-4ff7-8ed6-15d338f50f2d
我的nestJS知識有很大一部分是問ChatGPT了解的
尤其我搞不清楚module與那些providers
、Controller
之間的關係與關聯時
搞不清楚providers
和imports
的差別時
或是需要範例程式碼與逐行解釋裝飾器的時候
ChatGPT真的成了我很好的夥伴
下一篇!Controller!會一起談談有關裝飾器的事情